#include "sdk_config.h"
#include "flash.h"
#include "ota_flash.h"
#include "crc16.h"
#include "ota_protocol.h"
#include "osal.h"
#include "log.h"

#define APP_BOOT_INFO_SIZE				512			//file header size
#define BOOT_PARTITION_OFFSET			0x10 
#define APP_BANK_INFO_SECTION_SIZE   	0x1000

#define BOOTLOADER_UPDATEFLAG	0						 /**< BootLoader升级文件. */
#define APPLICATION_UPDATEFLAG	1						 /**< 应用程序升级文件. */
#define INVALID_UPDATAFLAG		0xff					 /**< 非合法. */

#define OTA_BUF_LEN 				256
__attribute__((section("_section_large_heap_"))) uint8 ota_buf[OTA_BUF_LEN];
static uint16 buf_len = 0;
static uint32 ota_buf_count = 0;

typedef struct {
	uint32_t flashBaseAddr;
	uint32_t partitionSize;
	uint32_t crc16Checksum;
	uint8_t  flashBuf[256];
}OtaFlashCtl_t;

typedef struct {
	uint32_t flag_updataNumber;		//Flag: 0xAA55 ,updateFile number ??0?,??BootLoader OTA?bank????;??1?,???bank??????;????????????
	uint32_t updateFile_addr;		//updateFile 1 address in flash
	uint32_t bin_lenght;			//ota bin file lenght
	uint32_t updateFile_type;		//0-BootLoader????;1-????
	uint32_t updateFile_checksum;	//file CRC16 checksum
	uint32_t updateFile_addr1;		//updateFile 1 address in flash
	uint32_t bin_lenght1;			//ota bin file lenght
	uint32_t updateFile_type1;		//0-BootLoader????;1-????
	uint32_t updateFile_checksum1;	//file CRC16 checksum
}OtaUpdateInfo_t;

OtaFlashCtl_t otaFlashCtl;
OtaUpdateInfo_t  bootloaderUpdateInfo = {0xff};
static uint8_t   write_code_flag= FALSE;

uint8_t bank_ota_flag =DUAL_BANK_OTA;

/**
* @fn ErrCode_t uint8 ota_data_write(uint8 *buf, uint8_t len,uint32_t total_size,uint32_t base_addr)
* @brief write data to OTA section.
* @param[buf] data buffer.
* @param[len] data length, the maximum value is 256.
* @param[total_size] bin size.
* @param[base_addr] flash base addr
* @return none
*/
void ota_data_write(uint8 *buf, uint8_t len,uint32_t total_size,uint32_t base_addr)
{	    
    int recv_len = 0; 
		uint32_t addr = base_addr;
		uint32_t buf_offset =0;
		if((buf_len + len) > OTA_BUF_LEN){
			osal_memcpy(ota_buf+buf_len,buf, OTA_BUF_LEN - buf_len);
			buf_offset = OTA_BUF_LEN - buf_len;
		}else{
			osal_memcpy(ota_buf+buf_len, buf, len);
		}
    buf_len += len;
		if(buf_len >= OTA_BUF_LEN)
		{
				HalFlashWrite(addr+ota_buf_count*OTA_BUF_LEN, ota_buf, OTA_BUF_LEN);
				//reset buf len
				if(buf_len == OTA_BUF_LEN){
					buf_len = 0;
				}else{
					osal_memcpy(ota_buf,buf+buf_offset, buf_len - OTA_BUF_LEN);
					buf_len = buf_len - OTA_BUF_LEN;
				}
				ota_buf_count ++;
		}
		recv_len = ota_buf_count*OTA_BUF_LEN + buf_len;
		if(total_size <=  recv_len)
		{
				if(buf_len != 0){
						HalFlashWrite(addr+ota_buf_count*OTA_BUF_LEN, ota_buf, buf_len);
				}
				//reset buf len
				buf_len = 0;
		
				
		}
}
/**
* @fn ErrCode_t otaFlash_WriteData(uint8_t *buf, uint32_t len)
* @brief write data to OTA section.
* @param[buf] data buffer.
* @param[len] data length, the maximum value is 256.
* @return ErrCode_t:return ERR_NONE when the operation is complete
*/
ErrCode_t otaFlash_WriteData(uint8_t *buf, uint32_t len,uint32_t total_size)
{
	uint32_t wrLen,poffset;
	uint8_t *ptr;
	ptr = buf;
	if(bank_ota_flag == SINGLE_BANK_OTA){
		if(write_code_flag)
		{
				// write code frimware
			ota_data_write(ptr, len,total_size-512,otaFlashCtl.flashBaseAddr);
			otaFlashCtl.partitionSize += len;
		}else{
				//write app boot info
				if((otaFlashCtl.partitionSize + len)>= APP_BOOT_INFO_SIZE)
				{
					wrLen = 512 - otaFlashCtl.partitionSize;
					ota_data_write(ptr, wrLen,512,otaFlashCtl.flashBaseAddr);
					otaFlashCtl.flashBaseAddr = FLASH_APP_ADDR;
					
					otaFlashCtl.partitionSize = 0;
					buf_len = 0;
					ota_buf_count = 0;
					write_code_flag = TRUE;
					if(wrLen != len){
						poffset = wrLen;
						wrLen = len - wrLen;
						ota_data_write(ptr+poffset, wrLen,total_size-512,otaFlashCtl.flashBaseAddr);
						otaFlashCtl.partitionSize += wrLen;
					}
				
				}else{
					ota_data_write(ptr, len,512,otaFlashCtl.flashBaseAddr);
					otaFlashCtl.partitionSize += len;
				}
		}
		
	}else{
		ota_data_write(ptr, len,total_size,otaFlashCtl.flashBaseAddr);
		otaFlashCtl.partitionSize += len;
	}

	otaFlashCtl.crc16Checksum = calcCrc16((int8 *)buf, len, otaFlashCtl.crc16Checksum);
	
	return ERR_NONE;
}
/**
* @fn ErrCode_t otaFlash_CalculateCRC16(uint8_t partitionNum, uint16_t crcValue)
* @brief check crc16 for ota bin.
* @param[buf] data buffer.
* @param[len] data length, the maximum value is 256.
* @return ErrCode_t:return ERR_NONE when the operation is complete
*/

ErrCode_t otaFlash_CalculateCRC16(uint8_t partitionNum, uint16_t crcValue)
{
	uint32_t i, len;
	uint16_t crc = 0xffff;
	if(bank_ota_flag == SINGLE_BANK_OTA)
	{
		otaFlashCtl.flashBaseAddr = FLASH_APP_BOOT_INFO_ADDR;
		for(i=0; i< APP_BOOT_INFO_SIZE ; i+=sizeof(otaFlashCtl.flashBuf)) {
		len = APP_BOOT_INFO_SIZE -i;
		if(len > sizeof(otaFlashCtl.flashBuf)) {
			len = sizeof(otaFlashCtl.flashBuf);
		}
		HalFlashRead(otaFlashCtl.flashBaseAddr +i, otaFlashCtl.flashBuf, len);
		crc = calcCrc16((int8 *)otaFlashCtl.flashBuf, len, crc);
		}
		
		otaFlashCtl.flashBaseAddr = FLASH_APP_ADDR;
		for(i=0; i< otaFlashCtl.partitionSize ; i+=sizeof(otaFlashCtl.flashBuf)) {
		len = otaFlashCtl.partitionSize -i;
		if(len > sizeof(otaFlashCtl.flashBuf)) {
			len = sizeof(otaFlashCtl.flashBuf);
		}
		HalFlashRead(otaFlashCtl.flashBaseAddr +i, otaFlashCtl.flashBuf, len);
		crc = calcCrc16((int8 *)otaFlashCtl.flashBuf, len, crc);
		}
	}else
	{
		for(i=0; i< otaFlashCtl.partitionSize ; i+=sizeof(otaFlashCtl.flashBuf)) {
		len = otaFlashCtl.partitionSize -i;
		if(len > sizeof(otaFlashCtl.flashBuf)) {
			len = sizeof(otaFlashCtl.flashBuf);
		}
		HalFlashRead(otaFlashCtl.flashBaseAddr +i, otaFlashCtl.flashBuf, len);
		crc = calcCrc16((int8 *)otaFlashCtl.flashBuf, len, crc);
		}
	}
	
	
	LOG("CheckCRC=%x,DownCRC=%x,SetCRC=%x\r\n",crc, otaFlashCtl.crc16Checksum, crcValue);
	if(crc == crcValue && crc == otaFlashCtl.crc16Checksum) {
		return ERR_NONE;
	}
	return ERR_OTA_CRC;
}




/**
* @fn setBootloaderUpdateInfo(void)
* @brief write ota update data .
* @param none
* @return none
*/
void setBootloaderUpdateInfo(void)
{
	if(bank_ota_flag == SINGLE_BANK_OTA){
		bank_ota_flag = DUAL_BANK_OTA;
		write_reg(0x1FFF18FC, 0);
		HalFlashErase(FLASH_BOOTLOADER_UPDATE_INFO_ADDR >> 12);
	}else{
		if (otaFlashCtl.flashBaseAddr == FLASH_CODE_BACKUP_ADDR)
		{
			bootloaderUpdateInfo.flag_updataNumber = 0x01aa55;
			bootloaderUpdateInfo.bin_lenght = otaFlashCtl.partitionSize;
			bootloaderUpdateInfo.updateFile_addr = otaFlashCtl.flashBaseAddr;
			if (otaInfo.imageType == OTA_IMAGE_TYPE_BOOTLOADER)
			{
				bootloaderUpdateInfo.updateFile_type = BOOTLOADER_UPDATEFLAG;
			}else{
				bootloaderUpdateInfo.updateFile_type = APPLICATION_UPDATEFLAG;
			}
			bootloaderUpdateInfo.updateFile_checksum = otaInfo.imageCRC16;
		}else{

			bootloaderUpdateInfo.flag_updataNumber = 0x02aa55;
			bootloaderUpdateInfo.bin_lenght1 = otaFlashCtl.partitionSize;
			bootloaderUpdateInfo.updateFile_addr1 = otaFlashCtl.flashBaseAddr;
			if (otaInfo.imageType == OTA_IMAGE_TYPE_BOOTLOADER)
			{
				bootloaderUpdateInfo.updateFile_type1 = BOOTLOADER_UPDATEFLAG;
			}else{
				bootloaderUpdateInfo.updateFile_type1 = APPLICATION_UPDATEFLAG;
			}
			bootloaderUpdateInfo.updateFile_checksum1= otaInfo.imageCRC16;
		}
			
			
		HalFlashErase(FLASH_BOOTLOADER_UPDATE_INFO_ADDR >> 12);
		HalFlashWrite(FLASH_BOOTLOADER_UPDATE_INFO_ADDR,(uint8_t *)&bootloaderUpdateInfo, 4);
		HalFlashWrite(FLASH_BOOTLOADER_UPDATE_INFO_ADDR + BOOT_PARTITION_OFFSET,(uint8_t *)&bootloaderUpdateInfo.updateFile_addr, sizeof(bootloaderUpdateInfo) - 4);
	
	
	}
	

}


/**
* @fn enter_sigle_bank_ota(void)
* @brief write  update_info_flag to enter sigle bank ota
* @param none
* @return none
*/
void enter_sigle_bank_ota(void)
{
		bootloaderUpdateInfo.flag_updataNumber = 0x0000aa55;

		HalFlashErase(FLASH_BOOTLOADER_UPDATE_INFO_ADDR >> 12);
		HalFlashWrite(FLASH_BOOTLOADER_UPDATE_INFO_ADDR,(uint8_t *)&bootloaderUpdateInfo, 4);

}

/**
* @fn otaFlash_Init(void)
* @brief ota init .
* @param none
* @return none
*/

void otaFlash_Init(void)
{
	uint16_t i,update_numb;
	bootloaderUpdateInfo.flag_updataNumber = *((volatile uint32_t *) (SPIF_BASE_ADDR+FLASH_BOOTLOADER_UPDATE_INFO_ADDR));
	HalFlashRead(FLASH_BOOTLOADER_UPDATE_INFO_ADDR + BOOT_PARTITION_OFFSET,(uint8_t *)&bootloaderUpdateInfo.updateFile_addr, 0x20);
	update_numb = bootloaderUpdateInfo.flag_updataNumber >> 16;
	LOG("imageType:%d\r\n",otaInfo.imageType);
	if ((bootloaderUpdateInfo.flag_updataNumber&0xFFFF) == 0xAA55)
	{
		//there are ota bin in flash
		if (update_numb == 1)
		{
			if (bootloaderUpdateInfo.updateFile_type == otaInfo.imageType/4)
			{
				otaFlashCtl.flashBaseAddr = FLASH_CODE_BACKUP_ADDR;
				
			}else{
				otaFlashCtl.flashBaseAddr = FLASH_CODE_BACKUP_ADDR + bootloaderUpdateInfo.bin_lenght;
			}
		
		}else if(update_numb == 2){
			for (i = 0; i < update_numb; ++i)
			{
				if (bootloaderUpdateInfo.updateFile_type == otaInfo.imageType/4)
				{
					otaFlashCtl.flashBaseAddr = FLASH_CODE_BACKUP_ADDR + i *bootloaderUpdateInfo.bin_lenght;
				}
			}
		}else if(update_numb == 0 && bank_ota_flag == SINGLE_BANK_OTA){
			otaFlashCtl.flashBaseAddr = FLASH_APP_BOOT_INFO_ADDR;
		}
		
	}else{
		otaFlashCtl.flashBaseAddr = FLASH_CODE_BACKUP_ADDR;
	}

	otaFlashCtl.partitionSize = 0;
	buf_len = 0;
	ota_buf_count = 0;
	otaFlashCtl.crc16Checksum = 0xFFFF;
	write_code_flag= FALSE;
}

void  erase_code_flash(void)
{
	uint32_t erase_num=0;
	int j;
	if(otaInfo.imageType == OTA_IMAGE_TYPE_BOOTLOADER)
	{
		erase_num = (otaInfo.blImageSize/4096)+((otaInfo.blImageSize%4096)?1:0);
	}else if(otaInfo.imageType == OTA_IMAGE_TYPE_APPLICATION){
		if(bank_ota_flag == SINGLE_BANK_OTA){
			erase_num = ((otaInfo.appImageSize-512)/4096)+(((otaInfo.appImageSize-512)%4096)?1:0);
		}else{
			erase_num = (otaInfo.appImageSize/4096)+((otaInfo.appImageSize%4096)?1:0);
		}
	}
	
	if(bank_ota_flag == SINGLE_BANK_OTA)
	{
		HalFlashErase((otaFlashCtl.flashBaseAddr)>>12);
		//erase mian file  addr
		for(j=0;j<erase_num;j++)
		{
				HalFlashErase((FLASH_APP_ADDR+j*4096)>>12);

		}
	}else{
		for(j=0;j<erase_num;j++)
		{
				HalFlashErase((otaFlashCtl.flashBaseAddr+j*4096)>>12);

		}
	}
	LOG("erase_num:%d,BaseAddr:%02x\r\n",erase_num,otaFlashCtl.flashBaseAddr);

}


